﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using GraphicsFramework.Entity;
using GraphicsFramework.Property;
using System.Drawing.Drawing2D;

namespace CanvasGDI.Graphics
{
    class GDI : GraphicsFramework.GraphicsFramework
    {

        private System.Drawing.Graphics graphics;

        public GDI(int width, int height, int gridsize, GraphicsFramework.Entity.Point2F gridvalue)
            : base(width, height, gridsize, gridvalue)
        {
        }

        public override void SetGraphics(object graphics)
        {
            this.graphics = (System.Drawing.Graphics)graphics;
        }

        #region Device Conversion

        public override GraphicsFramework.Enum.EnumKeys GetKey(object e)
        {
            KeyEventArgs key = (KeyEventArgs)e;

            switch (key.KeyCode)
            {
                //Numeric
                case Keys.D0:
                case Keys.NumPad0:
                    return GraphicsFramework.Enum.EnumKeys.Num0;
                case Keys.D1:
                case Keys.NumPad1:
                case Keys.Oem1:
                    return GraphicsFramework.Enum.EnumKeys.Num1;
                case Keys.D2:
                case Keys.NumPad2:
                case Keys.Oem2:
                    return GraphicsFramework.Enum.EnumKeys.Num2;
                case Keys.D3:
                case Keys.NumPad3:
                case Keys.Oem3:
                    return GraphicsFramework.Enum.EnumKeys.Num3;
                case Keys.D4:
                case Keys.NumPad4:
                case Keys.Oem4:
                    return GraphicsFramework.Enum.EnumKeys.Num4;
                case Keys.D5:
                case Keys.NumPad5:
                case Keys.Oem5:
                    return GraphicsFramework.Enum.EnumKeys.Num5;
                case Keys.D6:
                case Keys.NumPad6:
                case Keys.Oem6:
                    return GraphicsFramework.Enum.EnumKeys.Num6;
                case Keys.D7:
                case Keys.NumPad7:
                case Keys.Oem7:
                    return GraphicsFramework.Enum.EnumKeys.Num7;
                case Keys.D8:
                case Keys.NumPad8:
                case Keys.Oem8:
                    return GraphicsFramework.Enum.EnumKeys.Num8;
                case Keys.D9:
                case Keys.NumPad9:
                    return GraphicsFramework.Enum.EnumKeys.Num9;

                //Alphabets
                case Keys.A:
                    return GraphicsFramework.Enum.EnumKeys.A;
                case Keys.B:
                    return GraphicsFramework.Enum.EnumKeys.B;
                case Keys.C:
                    return GraphicsFramework.Enum.EnumKeys.C;
                case Keys.D:
                    return GraphicsFramework.Enum.EnumKeys.D;
                case Keys.E:
                    return GraphicsFramework.Enum.EnumKeys.E;
                case Keys.F:
                    return GraphicsFramework.Enum.EnumKeys.F;
                case Keys.G:
                    return GraphicsFramework.Enum.EnumKeys.G;
                case Keys.H:
                    return GraphicsFramework.Enum.EnumKeys.H;
                case Keys.I:
                    return GraphicsFramework.Enum.EnumKeys.I;
                case Keys.J:
                    return GraphicsFramework.Enum.EnumKeys.J;
                case Keys.K:
                    return GraphicsFramework.Enum.EnumKeys.K;
                case Keys.L:
                    return GraphicsFramework.Enum.EnumKeys.L;
                case Keys.M:
                    return GraphicsFramework.Enum.EnumKeys.M;
                case Keys.N:
                    return GraphicsFramework.Enum.EnumKeys.N;
                case Keys.O:
                    return GraphicsFramework.Enum.EnumKeys.O;
                case Keys.P:
                    return GraphicsFramework.Enum.EnumKeys.P;
                case Keys.Q:
                    return GraphicsFramework.Enum.EnumKeys.Q;
                case Keys.R:
                    return GraphicsFramework.Enum.EnumKeys.R;
                case Keys.S:
                    return GraphicsFramework.Enum.EnumKeys.S;
                case Keys.T:
                    return GraphicsFramework.Enum.EnumKeys.T;
                case Keys.U:
                    return GraphicsFramework.Enum.EnumKeys.U;
                case Keys.V:
                    return GraphicsFramework.Enum.EnumKeys.V;
                case Keys.W:
                    return GraphicsFramework.Enum.EnumKeys.W;
                case Keys.X:
                    return GraphicsFramework.Enum.EnumKeys.X;
                case Keys.Y:
                    return GraphicsFramework.Enum.EnumKeys.Y;
                case Keys.Z:
                    return GraphicsFramework.Enum.EnumKeys.Z;

                //Named Keys
                case Keys.Enter:
                    return GraphicsFramework.Enum.EnumKeys.Enter;
                case Keys.Tab:
                    return GraphicsFramework.Enum.EnumKeys.Tab;
                case Keys.Space:
                    return GraphicsFramework.Enum.EnumKeys.Space;
                case Keys.Escape:
                    return GraphicsFramework.Enum.EnumKeys.Escape;
                case Keys.PrintScreen:
                    return GraphicsFramework.Enum.EnumKeys.PrintScreen;
                case Keys.Scroll:
                    return GraphicsFramework.Enum.EnumKeys.ScrollLock;
                case Keys.Pause:
                    return GraphicsFramework.Enum.EnumKeys.Pause;
                case Keys.Insert:
                    return GraphicsFramework.Enum.EnumKeys.Insert;
                case Keys.Delete:
                    return GraphicsFramework.Enum.EnumKeys.Delete;
                case Keys.Home:
                    return GraphicsFramework.Enum.EnumKeys.Home;
                case Keys.End:
                    return GraphicsFramework.Enum.EnumKeys.End;
                case Keys.PageUp:
                    return GraphicsFramework.Enum.EnumKeys.PageUp;
                case Keys.PageDown:
                    return GraphicsFramework.Enum.EnumKeys.PageDown;

                //+, -, *, -
                case Keys.Add:
                case Keys.Oemplus:
                    return GraphicsFramework.Enum.EnumKeys.Add;
                case Keys.Subtract:
                    return GraphicsFramework.Enum.EnumKeys.Subtract;
                case Keys.OemMinus:
                    return GraphicsFramework.Enum.EnumKeys.Subtract;

                //Arrows
                case Keys.Left:
                    return GraphicsFramework.Enum.EnumKeys.Left;
                case Keys.Right:
                    return GraphicsFramework.Enum.EnumKeys.Right;
                case Keys.Up:
                    return GraphicsFramework.Enum.EnumKeys.Up;
                case Keys.Down:
                    return GraphicsFramework.Enum.EnumKeys.Down;

                //Functions
                case Keys.F1:
                    return GraphicsFramework.Enum.EnumKeys.F1;
                case Keys.F2:
                    return GraphicsFramework.Enum.EnumKeys.F2;
                case Keys.F3:
                    return GraphicsFramework.Enum.EnumKeys.F3;
                case Keys.F4:
                    return GraphicsFramework.Enum.EnumKeys.F4;
                case Keys.F5:
                    return GraphicsFramework.Enum.EnumKeys.F5;
                case Keys.F6:
                    return GraphicsFramework.Enum.EnumKeys.F6;
                case Keys.F7:
                    return GraphicsFramework.Enum.EnumKeys.F7;
                case Keys.F8:
                    return GraphicsFramework.Enum.EnumKeys.F8;
                case Keys.F9:
                    return GraphicsFramework.Enum.EnumKeys.F9;
                case Keys.F10:
                    return GraphicsFramework.Enum.EnumKeys.F10;
                case Keys.F11:
                    return GraphicsFramework.Enum.EnumKeys.F11;
                case Keys.F12:
                    return GraphicsFramework.Enum.EnumKeys.F12;
                case Keys.F13:
                    return GraphicsFramework.Enum.EnumKeys.F13;
                case Keys.F14:
                    return GraphicsFramework.Enum.EnumKeys.F14;
                case Keys.F15:
                    return GraphicsFramework.Enum.EnumKeys.F15;
                case Keys.F16:
                    return GraphicsFramework.Enum.EnumKeys.F16;
                case Keys.F17:
                    return GraphicsFramework.Enum.EnumKeys.F17;
                case Keys.F18:
                    return GraphicsFramework.Enum.EnumKeys.F18;
                case Keys.F19:
                    return GraphicsFramework.Enum.EnumKeys.F19;
                case Keys.F20:
                    return GraphicsFramework.Enum.EnumKeys.F20;
                case Keys.F21:
                    return GraphicsFramework.Enum.EnumKeys.F21;
                case Keys.F22:
                    return GraphicsFramework.Enum.EnumKeys.F22;
                case Keys.F23:
                    return GraphicsFramework.Enum.EnumKeys.F23;
                case Keys.F24:
                    return GraphicsFramework.Enum.EnumKeys.F24;

                //Default
                default:
                    return GraphicsFramework.Enum.EnumKeys.None;
            }

        }

        public override GraphicsFramework.Enum.EnumMouseButtons GetMouseButton(object e)
        {
            MouseEventArgs mouse = (MouseEventArgs)e;

            switch (mouse.Button)
            {
                case MouseButtons.Left:
                    return GraphicsFramework.Enum.EnumMouseButtons.LEFT;
                case MouseButtons.Middle:
                    return GraphicsFramework.Enum.EnumMouseButtons.MIDDLE;
                case MouseButtons.Right:
                    return GraphicsFramework.Enum.EnumMouseButtons.RIGHT;
            }

            return GraphicsFramework.Enum.EnumMouseButtons.NONE;
        }

        #endregion

        #region Property Conversion

        public DashStyle GetDashStyle(GraphicsFramework.Enum.EnumDashStyle dash)
        {
            switch (dash)
            {
                case GraphicsFramework.Enum.EnumDashStyle.DASH:
                    return DashStyle.Dash;
                case GraphicsFramework.Enum.EnumDashStyle.DASHDOT:
                    return DashStyle.DashDot;
                case GraphicsFramework.Enum.EnumDashStyle.DASHDOTDOT:
                    return DashStyle.DashDotDot;
                case GraphicsFramework.Enum.EnumDashStyle.DOT:
                    return DashStyle.Dot;
                default:
                    return DashStyle.Solid;
            }
        }

        private Pen GetPen(DrawProperty property)
        {
            return new Pen(Color.FromArgb(property.LineColor.Alpha, property.LineColor.Red, property.LineColor.Green, property.LineColor.Blue), GetLineWidth(property));
        }

        private Brush GetBrush(DrawProperty property)
        {
            return new SolidBrush(Color.FromArgb(property.FillColor.Alpha, property.FillColor.Red, property.FillColor.Green, property.FillColor.Blue));
        }

        private Point[] GetPoints(IList<Point2F> points)
        {
            Point[] cpoint = new Point[points.Count];

            for (int count = 0; count < points.Count; count++)
                cpoint[count] = new Point(ToCoordX(points[count].X), ToCoordY(points[count].Y));

            return cpoint;
        }

        private Point GetPoint(Point2F point)
        {
            return new Point(ToCoordX(point.X), ToCoordY(point.Y));
        }

        private Font GetFont(TextProperty property)
        {
            int style = 0;

            if (property.Bold)
                style += (int)FontStyle.Bold;

            if (property.Italic)
                style += (int)FontStyle.Italic;

            if (property.Underline)
                style += (int)FontStyle.Underline;

            if (property.StrikeOut)
                style += (int)FontStyle.Strikeout;

            Font font = new Font(property.Font, property.Size * zoomvalue, (FontStyle)style);

            return font;
        }

        #endregion

        #region Measure

        //public override Point2I MeasureStringExactWidth(TextProperty textproperty, string text)
        //{
        //    StringFormat format = new System.Drawing.StringFormat();
        //    RectangleF graphic = new System.Drawing.RectangleF(0, 0, int.MaxValue, int.MaxValue);
        //    CharacterRange[] ranges = { new System.Drawing.CharacterRange(0, text.Length) };
        //    Region[] regions = new System.Drawing.Region[1];
        //    Font font = GetFont(textproperty);

        //    format.SetMeasurableCharacterRanges(ranges);
        //    regions = graphics.MeasureCharacterRanges(text, font, graphic, format);
        //    graphic = regions[0].GetBounds(graphics);
        //    SizeF size = graphics.MeasureString(text, font);

        //    Point2I point = new Point2I((int)(Math.Truncate(graphic.Right + 1)), (int)size.Height);
        //    return point;
        //}

        public override Point2I MeasureString(TextProperty textproperty, string text)
        {
            Font font = GetFont(textproperty);
            SizeF size = graphics.MeasureString(text, font);

            Point2I point = new Point2I((int)(size.Width), (int)size.Height);
            return point;
        }
        
        #endregion
                       
        #region Line
        
        public override void BasicLine(DrawProperty property, Point2I point1, Point2I point2)
        {
            Pen pen = GetPen(property);
            graphics.DrawLine(pen, new Point(point1.X, point1.Y), new Point(point2.X, point2.Y));
        }

        public override void DrawLine(DrawProperty property, Point2F point1, Point2F point2)
        {
            Pen pen = GetPen(property);
            Point Point1 = GetPoint(point1);
            Point Point2 = GetPoint(point2);

            graphics.DrawLine(pen, Point1, Point2);
        }

        #endregion

        #region Circle

        public override void BasicCircle(DrawProperty property, Point2I point, float radius)
        {
            float hwidth = radius / 2;

            if (property.ShowFill)
            {
                Brush brush = GetBrush(property);
                graphics.FillEllipse(brush, point.X - hwidth, point.Y - hwidth, radius, radius);
            }

            if (property.ShowOutline)
            {
                Pen pen = GetPen(property);
                graphics.DrawEllipse(pen, point.X - hwidth, point.Y - hwidth, radius, radius);
            }
        }

        public override void BasicCircle(DrawProperty property, Point2F point, float radius)
        {
            float hwidth = radius / 2;

            if (property.ShowFill)
            {
                Brush brush = GetBrush(property);
                graphics.FillEllipse(brush, point.X - hwidth, point.Y - hwidth, radius, radius);
            }

            if (property.ShowOutline)
            {
                Pen pen = GetPen(property);
                graphics.DrawEllipse(pen, point.X - hwidth, point.Y - hwidth, radius, radius);
            }
        }
        
        public override void DrawCircle(DrawProperty property, Point2F point, float radius)
        {
            radius *= Gridsize;
            float diameter = radius * 2;

            if (property.ShowFill)
            {
                Brush brush = GetBrush(property);
                graphics.FillEllipse(brush, ToCoordX(point.X) - radius, ToCoordY(point.Y) - radius, diameter, diameter);
            }
            
            if (property.ShowOutline)
            {
                Pen pen = GetPen(property);
                graphics.DrawEllipse(pen, ToCoordX(point.X) - radius, ToCoordY(point.Y) - radius, diameter, diameter);
            }
        }

        #endregion

        #region Ellipse

        public override void BasicEllipse(DrawProperty property, Point2I point, int width, int height)
        {
            throw new NotImplementedException();
        }

        public override void BasicEllipse(DrawProperty property, Point2I point1, Point2I point2)
        {
            throw new NotImplementedException();
        }

        public override void DrawEllipse(DrawProperty property, Point2F point1, float width, float height)
        {
            throw new NotImplementedException();
        }

        public override void DrawEllipse(DrawProperty property, Point2F point1, Point2F point2)
        {
            throw new NotImplementedException();
        }
        
        #endregion

        #region Rectangle

        public override void BasicRectangle(DrawProperty property, Point2F point, float width, float height)
        {

            float hwidth = width / 2;
            float hheight = height / 2;

            if (property.ShowFill)
            {
                Brush brush = GetBrush(property);
                graphics.FillRectangle(brush, point.X - hwidth, point.Y - hheight, width, height);
            }

            if (property.ShowOutline)
            {
                Pen pen = GetPen(property);
                graphics.DrawRectangle(pen, point.X - hwidth, point.Y - hheight, width, height);
            }
        
        }

        public override void BasicRectangle(DrawProperty property, Point2I point, int width, int height)
        {

            float hwidth = width / 2;
            float hheight = height / 2;

            if (property.ShowFill)
            {
                Brush brush = GetBrush(property);
                graphics.FillRectangle(brush, point.X - hwidth, point.Y - hheight, width, height);
            }

            if (property.ShowOutline)
            {
                Pen pen = GetPen(property);
                graphics.DrawRectangle(pen, point.X - hwidth, point.Y - hheight, width, height);
            }

        }

        public override void DrawRectangle(DrawProperty property, Point2I point, int width, int height)
        {
        
            width *= Gridsize;
            height *= Gridsize;

            float hwidth = width / 2;
            float hheight = height / 2;

            if (property.ShowFill)
            {
                Brush brush = GetBrush(property);
                graphics.FillRectangle(brush, ToCoordX(point.X) - hwidth, ToCoordY(point.Y) - hheight, width, height);
            }

            if (property.ShowOutline)
            {
                Pen pen = GetPen(property);
                graphics.DrawRectangle(pen, ToCoordX(point.X) - hwidth, ToCoordY(point.Y) - hheight, width, height);
            }

        }

        public override void DrawRectangle(DrawProperty property, Point2F point, float width, float height)
        {

            width *= Gridsize;
            height *= Gridsize;

            float hwidth = width / 2;
            float hheight = height / 2;

            if (property.ShowFill)
            {
                Brush brush = GetBrush(property);
                graphics.FillRectangle(brush, ToCoordX(point.X) - hwidth, ToCoordY(point.Y) - hheight, width, height);
            }

            if (property.ShowOutline)
            {
                Pen pen = GetPen(property);
                graphics.DrawRectangle(pen, ToCoordX(point.X) - hwidth, ToCoordY(point.Y) - hheight, width, height);
            }
        
        }

        #endregion

        #region Poyline
        
        public override void DrawPolyline(DrawProperty property, IList<Point2F> points)
        {
            Pen pen = GetPen(property);
            Point[] cpoint = GetPoints(points);

            graphics.DrawLines(pen, cpoint);
        }

        #endregion

        #region Polygon
        
        public override void DrawPolygon(DrawProperty property, IList<Point2F> points)
        {
            Point[] cpoint = GetPoints(points);

            if (property.ShowFill)
            {
                Brush brush = GetBrush(property);
                graphics.FillPolygon(brush, cpoint);
            }

            if (property.ShowOutline)
            {
                Pen pen = GetPen(property);
                graphics.DrawPolygon(pen, cpoint);
            }
        }

        #endregion

        #region String
        
        public override void DrawText(DrawProperty property, TextProperty textproperty, Point2F point, string text)
        {
            Brush brush = GetBrush(property);
            Point cpoint = new Point(ToCoordX(point.X), ToCoordY(point.Y));
            Font font = GetFont(textproperty);

            graphics.DrawString(text, font, brush, cpoint);
        }

        #endregion

    }
}
